#include "voe_apm.h"
#include "shared_data.h"
#include "trace.h"
#include "logging.h"
#include "critical_section_wrapper.h"
#include "transmit_mixer.h"
#include "channel.h"

static const EcModes kDefaultEcMode = kEcAec;

VoEAudioProcessingImpl::VoEAudioProcessingImpl(SharedData* shared)
  :_isAecMode(kDefaultEcMode == kEcAec), _shared(shared) {
  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "VoEAudioProcessingImpl::VoEAudioProcessingImpl() - ctor");
}

VoEAudioProcessingImpl::~VoEAudioProcessingImpl() {
  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "VoEAudioProcessingImpl::~VoEAudioProcessingImpl() - dtor");
}


int VoEAudioProcessingImpl::SetNsStatus(bool enable, NsModes mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetNsStatus(enable=%d, mode=%d)", enable, mode);
#ifdef WEBRTC_VOICE_ENGINE_NR
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  NoiseSuppression::Level nsLevel(
    (NoiseSuppression::Level)WEBRTC_VOICE_ENGINE_NS_DEFAULT_MODE);
  switch (mode) {
  case kNsDefault:
    nsLevel = (NoiseSuppression::Level)WEBRTC_VOICE_ENGINE_NS_DEFAULT_MODE;
    break;
  case kNsUnchanged:
    nsLevel = _shared->audio_processing()->noise_suppression()->level();
    break;
  case kNsConference:
    nsLevel = NoiseSuppression::kHigh;
    break;
  case kNsLowSuppression:
    nsLevel = NoiseSuppression::kLow;
    break;
  case kNsModerateSuppression:
    nsLevel = NoiseSuppression::kModerate;
    break;
  case kNsHighSuppression:
    nsLevel = NoiseSuppression::kHigh;
    break;
  case kNsVeryHighSuppression:
    nsLevel = NoiseSuppression::kVeryHigh;
    break;
  }

  if (_shared->audio_processing()->noise_suppression()->
    set_level(nsLevel) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetNsStatus() failed to set Ns mode");
      return -1;
  }
  if (_shared->audio_processing()->noise_suppression()->Enable(enable) != 0) {
    _shared->SetLastError(VE_APM_ERROR, kTraceError,
      "SetNsStatus() failed to set Ns state");
    return -1;
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetNsStatus() Ns is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetNsStatus(bool& enabled, NsModes& mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetNsStatus(enabled=?, mode=?)");
#ifdef WEBRTC_VOICE_ENGINE_NR
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  bool enable(false);
  NoiseSuppression::Level nsLevel(
    (NoiseSuppression::Level)WEBRTC_VOICE_ENGINE_NS_DEFAULT_MODE);

  enable = _shared->audio_processing()->noise_suppression()->is_enabled();
  nsLevel = _shared->audio_processing()->noise_suppression()->level();

  enabled = enable;

  switch (nsLevel) {
  case NoiseSuppression::kLow:
    mode = kNsLowSuppression;
    break;
  case NoiseSuppression::kModerate:
    mode = kNsModerateSuppression;
    break;
  case NoiseSuppression::kHigh:
    mode = kNsHighSuppression;
    break;
  case NoiseSuppression::kVeryHigh:
    mode = kNsVeryHighSuppression;
    break;
  }

  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetNsStatus() => enabled=% d, mode=%d", enabled, mode);
  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetNsStatus() Ns is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetAgcStatus(bool enable, AgcModes mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetAgcStatus(enable=%d, mode=%d)", enable, mode);
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

#if defined(WEBRTC_IOS) || defined(ATA) || defined(WEBRTC_ANDROID)
  if (mode == kAgcAdaptiveAnalog) {
    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
      "SetAgcStatus() invalid Agc mode for mobile device");
    return -1;
  }
#endif

  GainControl::Mode agcMode(
    (GainControl::Mode)WEBRTC_VOICE_ENGINE_AGC_DEFAULT_MODE);
  switch (mode) {
  case kAgcDefault:
    agcMode = (GainControl::Mode)WEBRTC_VOICE_ENGINE_AGC_DEFAULT_MODE;
    break;
  case kAgcUnchanged:
    agcMode = _shared->audio_processing()->gain_control()->mode();;
    break;
  case kAgcFixedDigital:
    agcMode = GainControl::kFixedDigital;
    break;
  case kAgcAdaptiveAnalog:
    agcMode = GainControl::kAdaptiveAnalog;
    break;
  case kAgcAdaptiveDigital:
    agcMode = GainControl::kAdaptiveDigital;
    break;
  }

  if (_shared->audio_processing()->gain_control()->set_mode(agcMode) != 0) {
    _shared->SetLastError(VE_APM_ERROR, kTraceError,
      "SetAgcStatus() failed to set Agc mode");
    return -1;
  }
  if (_shared->audio_processing()->gain_control()->Enable(enable) != 0) {
    _shared->SetLastError(VE_APM_ERROR, kTraceError,
      "SetAgcStatus() failed to set Agc state");
    return -1;
  }

  if (agcMode != GainControl::kFixedDigital) {
    // Set Agc state in the ADM when adaptive Agc mode has been selected.
    // Note that we also enable the ADM Agc when Adaptive Digital mode is
    // used since we want to be able to provide the APM with updated mic
    // levels when the user modifies the mic level manually.
    if (_shared->audio_device()->SetAGC(enable) != 0) {
      _shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
        kTraceWarning, "SetAgcStatus() failed to set Agc mode");
    }
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetAgcStatus() Agc is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetAgcStatus(bool& enabled, AgcModes& mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetAgcStatus(enabled=?, mode=?)");
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  bool enable(false);
  GainControl::Mode agcMode(
    (GainControl::Mode)WEBRTC_VOICE_ENGINE_AGC_DEFAULT_MODE);

  enable = _shared->audio_processing()->gain_control()->is_enabled();
  agcMode = _shared->audio_processing()->gain_control()->mode();

  enabled = enable;

  switch (agcMode) {
  case GainControl::kFixedDigital:
    mode = kAgcFixedDigital;
    break;
  case GainControl::kAdaptiveAnalog:
    mode = kAgcAdaptiveAnalog;
    break;
  case GainControl::kAdaptiveDigital:
    mode = kAgcAdaptiveDigital;
    break;
  }

  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetAgcStatus() => enabled=%d, mode=%d", enabled, mode);
  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetAgcStatus() Agc is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetAgcConfig(const AgcConfig config) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetAgcConfig()");
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  if (_shared->audio_processing()->gain_control()->set_target_level_dbfs(
    config.targetLeveldBOv) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetAgcConfig() failed to set target peak |level|"
        " (or envelope) of the Agc");
      return -1;
  }
  if (_shared->audio_processing()->gain_control()->set_compression_gain_db(
    config.digitalCompressionGaindB) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetAgcConfig() failed to set the range in |gain| "
        "the digital compression stage may apply");
      return -1;
  }
  if (_shared->audio_processing()->gain_control()->enable_limiter(
    config.limiterEnable) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetAgcConfig() failed to set hard limiter to the signal");
      return -1;
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetAgcConfig() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetAgcConfig(AgcConfig& config) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetAgcConfig(config=?)");
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  config.targetLeveldBOv =
    _shared->audio_processing()->gain_control()->target_level_dbfs();
  config.digitalCompressionGaindB =
    _shared->audio_processing()->gain_control()->compression_gain_db();
  config.limiterEnable =
    _shared->audio_processing()->gain_control()->is_limiter_enabled();

  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetAgcConfig() => targetLeveldBOv=%u, "
    "digitalCompressionGaindB=%u, limiterEnable=%d",
    config.targetLeveldBOv,
    config.digitalCompressionGaindB,
    config.limiterEnable);

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetAgcConfig() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetRxNsStatus(int channel, bool enable, NsModes mode) {
  LOG_API3(channel, enable, mode);
#ifdef WEBRTC_VOICE_ENGINE_NR
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "SetRxNsStatus() failed to locate channel");
    return -1;
  }
  return channelPtr->SetRxNsStatus(enable, mode);
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetRxNsStatus() NS is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetRxNsStatus(int channel, bool& enabled, NsModes& mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetRxNsStatus(channel=%d, enable=?, mode=?)", channel);
#ifdef WEBRTC_VOICE_ENGINE_NR
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "GetRxNsStatus() failed to locate channel");
    return -1;
  }
  return channelPtr->GetRxNsStatus(enabled, mode);
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetRxNsStatus() NS is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetRxAgcStatus(int channel, bool enable, AgcModes mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetRxAgcStatus(channel=%d, enable=%d, mode=%d)",
    channel, (int)enable, (int)mode);
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "SetRxAgcStatus() failed to locate channel");
    return -1;
  }
  return channelPtr->SetRxAgcStatus(enable, mode);
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetRxAgcStatus() Agc is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetRxAgcStatus(int channel,
  bool& enabled,
  AgcModes& mode) {
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
      "GetRxAgcStatus(channel=%d, enable=?, mode=?)", channel);
#ifdef WEBRTC_VOICE_ENGINE_AGC
    if (!_shared->statistics().Initialized()) {
      _shared->SetLastError(VE_NOT_INITED, kTraceError);
      return -1;
    }

    voe::ScopedChannel sc(_shared->channel_manager(), channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL) {
      _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
        "GetRxAgcStatus() failed to locate channel");
      return -1;
    }
    return channelPtr->GetRxAgcStatus(enabled, mode);
#else
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
      "GetRxAgcStatus() Agc is not supported");
    return -1;
#endif
}

int VoEAudioProcessingImpl::SetRxAgcConfig(int channel,
  const AgcConfig config) {
    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
      "SetRxAgcConfig(channel=%d)", channel);
#ifdef WEBRTC_VOICE_ENGINE_AGC
    if (!_shared->statistics().Initialized()) {
      _shared->SetLastError(VE_NOT_INITED, kTraceError);
      return -1;
    }

    voe::ScopedChannel sc(_shared->channel_manager(), channel);
    voe::Channel* channelPtr = sc.ChannelPtr();
    if (channelPtr == NULL) {
      _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
        "SetRxAgcConfig() failed to locate channel");
      return -1;
    }
    return channelPtr->SetRxAgcConfig(config);
#else
    _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
      "SetRxAgcConfig() Agc is not supported");
    return -1;
#endif
}

int VoEAudioProcessingImpl::GetRxAgcConfig(int channel, AgcConfig& config) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetRxAgcConfig(channel=%d)", channel);
#ifdef WEBRTC_VOICE_ENGINE_AGC
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "GetRxAgcConfig() failed to locate channel");
    return -1;
  }
  return channelPtr->GetRxAgcConfig(config);
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetRxAgcConfig() Agc is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetEcStatus(bool enable, EcModes mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetEcStatus(enable=%d, mode=%d)", enable, mode);
#ifdef WEBRTC_VOICE_ENGINE_ECHO
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  // AEC mode
  if ((mode == kEcDefault) ||
    (mode == kEcConference) ||
    (mode == kEcAec) ||
    ((mode == kEcUnchanged) &&
    (_isAecMode == true))) {
      if (enable) {
        // Disable the AECM before enable the AEC
        if (_shared->audio_processing()->echo_control_mobile()->is_enabled()) {
          _shared->SetLastError(VE_APM_ERROR, kTraceWarning,
            "SetEcStatus() disable AECM before enabling AEC");
          if (_shared->audio_processing()->echo_control_mobile()->
            Enable(false) != 0) {
              _shared->SetLastError(VE_APM_ERROR, kTraceError,
                "SetEcStatus() failed to disable AECM");
              return -1;
          }
        }
      }
      if (_shared->audio_processing()->echo_cancellation()->Enable(enable) != 0) {
        _shared->SetLastError(VE_APM_ERROR, kTraceError,
          "SetEcStatus() failed to set AEC state");
        return -1;
      }
      if (mode == kEcConference) {
        if (_shared->audio_processing()->echo_cancellation()->
          set_suppression_level(EchoCancellation::kHighSuppression) != 0) {
            _shared->SetLastError(VE_APM_ERROR, kTraceError,
              "SetEcStatus() failed to set aggressiveness to high");
            return -1;
        }
      } else {
        if (_shared->audio_processing()->echo_cancellation()->
          set_suppression_level(
          EchoCancellation::kModerateSuppression) != 0) {
            _shared->SetLastError(VE_APM_ERROR, kTraceError,
              "SetEcStatus() failed to set aggressiveness to moderate");
            return -1;
        }
      }

      _isAecMode = true;
  } else if ((mode == kEcAecm) ||
    ((mode == kEcUnchanged) &&
    (_isAecMode == false))) {
      if (enable) {
        // Disable the AEC before enable the AECM
        if (_shared->audio_processing()->echo_cancellation()->is_enabled()) {
          _shared->SetLastError(VE_APM_ERROR, kTraceWarning,
            "SetEcStatus() disable AEC before enabling AECM");
          if (_shared->audio_processing()->echo_cancellation()->
            Enable(false) != 0) {
              _shared->SetLastError(VE_APM_ERROR, kTraceError,
                "SetEcStatus() failed to disable AEC");
              return -1;
          }
        }
      }
      if (_shared->audio_processing()->echo_control_mobile()->
        Enable(enable) != 0) {
          _shared->SetLastError(VE_APM_ERROR, kTraceError,
            "SetEcStatus() failed to set AECM state");
          return -1;
      }
      _isAecMode = false;
  } else {
    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
      "SetEcStatus() invalid EC mode");
    return -1;
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetEcStatus() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetEcStatus(bool& enabled, EcModes& mode) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetEcStatus()");
#ifdef WEBRTC_VOICE_ENGINE_ECHO
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  if (_isAecMode == true) {
    mode = kEcAec;
    enabled = _shared->audio_processing()->echo_cancellation()->is_enabled();
  } else {
    mode = kEcAecm;
    enabled = _shared->audio_processing()->echo_control_mobile()->
      is_enabled();
  }

  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetEcStatus() => enabled=%i, mode=%i",
    enabled, (int)mode);
  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetEcStatus() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::SetAecmMode(AecmModes mode, bool enableCNG) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetAECMMode(mode = %d)", mode);
#ifdef WEBRTC_VOICE_ENGINE_ECHO
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  EchoControlMobile::RoutingMode aecmMode(
    EchoControlMobile::kQuietEarpieceOrHeadset);

  switch (mode) {
  case kAecmQuietEarpieceOrHeadset:
    aecmMode = EchoControlMobile::kQuietEarpieceOrHeadset;
    break;
  case kAecmEarpiece:
    aecmMode = EchoControlMobile::kEarpiece;
    break;
  case kAecmLoudEarpiece:
    aecmMode = EchoControlMobile::kLoudEarpiece;
    break;
  case kAecmSpeakerphone:
    aecmMode = EchoControlMobile::kSpeakerphone;
    break;
  case kAecmLoudSpeakerphone:
    aecmMode = EchoControlMobile::kLoudSpeakerphone;
    break;
  }


  if (_shared->audio_processing()->echo_control_mobile()->
    set_routing_mode(aecmMode) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetAECMMode() failed to set AECM routing mode");
      return -1;
  }
  if (_shared->audio_processing()->echo_control_mobile()->
    enable_comfort_noise(enableCNG) != 0) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "SetAECMMode() failed to set comfort noise state for AECM");
      return -1;
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "SetAECMMode() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::GetAecmMode(AecmModes& mode, bool& enabledCNG) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetAECMMode(mode=?)");
#ifdef WEBRTC_VOICE_ENGINE_ECHO
  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }

  enabledCNG = false;

  EchoControlMobile::RoutingMode aecmMode =
    _shared->audio_processing()->echo_control_mobile()->routing_mode();
  enabledCNG = _shared->audio_processing()->echo_control_mobile()->
    is_comfort_noise_enabled();

  switch (aecmMode) {
  case EchoControlMobile::kQuietEarpieceOrHeadset:
    mode = kAecmQuietEarpieceOrHeadset;
    break;
  case EchoControlMobile::kEarpiece:
    mode = kAecmEarpiece;
    break;
  case EchoControlMobile::kLoudEarpiece:
    mode = kAecmLoudEarpiece;
    break;
  case EchoControlMobile::kSpeakerphone:
    mode = kAecmSpeakerphone;
    break;
  case EchoControlMobile::kLoudSpeakerphone:
    mode = kAecmLoudSpeakerphone;
    break;
  }

  return 0;
#else
  _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
    "GetAECMMode() EC is not supported");
  return -1;
#endif
}

int VoEAudioProcessingImpl::EnableHighPassFilter(bool enable) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "EnableHighPassFilter(%d)", enable);
  if (_shared->audio_processing()->high_pass_filter()->Enable(enable) !=
    AudioProcessing::kNoError) {
      _shared->SetLastError(VE_APM_ERROR, kTraceError,
        "HighPassFilter::Enable() failed.");
      return -1;
  }

  return 0;
}

bool VoEAudioProcessingImpl::IsHighPassFilterEnabled() {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "IsHighPassFilterEnabled()");
  return _shared->audio_processing()->high_pass_filter()->is_enabled();
}

int VoEAudioProcessingImpl::SetVADStatus(int channel, bool enable,
  VadModes mode, bool disableDTX) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "SetVADStatus(channel=%i, enable=%i, mode=%i, disableDTX=%i)",
    channel, enable, mode, disableDTX);

  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }
  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "SetVADStatus failed to locate channel");
    return -1;
  }

  ACMVADMode vadMode(VADNormal);
  switch (mode) {
  case kVadConventional:
    vadMode = VADNormal;
    break;
  case kVadAggressiveLow:
    vadMode = VADLowBitrate;
    break;
  case kVadAggressiveMid:
    vadMode = VADAggr;
    break;
  case kVadAggressiveHigh:
    vadMode = VADVeryAggr;
    break;
  }
  return channelPtr->SetVADStatus(enable, vadMode, disableDTX);
}

int VoEAudioProcessingImpl::GetVADStatus(int channel, bool& enabled,
  VadModes& mode, bool& disabledDTX) {
  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
    "GetVADStatus(channel=%i)", channel);

  if (!_shared->statistics().Initialized()) {
    _shared->SetLastError(VE_NOT_INITED, kTraceError);
    return -1;
  }
  voe::ScopedChannel sc(_shared->channel_manager(), channel);
  voe::Channel* channelPtr = sc.ChannelPtr();
  if (channelPtr == NULL) {
    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
      "GetVADStatus failed to locate channel");
    return -1;
  }

  ACMVADMode vadMode;
  int ret = channelPtr->GetVADStatus(enabled, vadMode, disabledDTX);

  if (ret != 0) {
    _shared->SetLastError(VE_INVALID_OPERATION, kTraceError,
      "GetVADStatus failed to get VAD mode");
    return -1;
  }
  switch (vadMode) {
  case VADNormal:
    mode = kVadConventional;
    break;
  case VADLowBitrate:
    mode = kVadAggressiveLow;
    break;
  case VADAggr:
    mode = kVadAggressiveMid;
    break;
  case VADVeryAggr:
    mode = kVadAggressiveHigh;
    break;
  }

  return 0;
}
